package com.db.client.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.activation.MailcapCommandMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTable;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import java.awt.GridLayout;
import java.awt.Component;
import javax.swing.Box;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;

import com.db.client.data.ClientOrder;
import com.db.client.data.Execution;
import com.db.protocol.tags.OrderStatus;
import com.db.protocol.tags.OrderType;
import com.db.protocol.tags.OrderSide;
import com.db.protocol.tags.RejectReason;

import javax.swing.JTabbedPane;
import net.miginfocom.swing.MigLayout;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.RowSpec;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.awt.Color;
import javax.swing.table.DefaultTableModel;
import javax.swing.UIManager;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.logging.Logger;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class MainClient extends JFrame implements GuiView {
	
	private static Logger log = Logger.getLogger(MainClient.class.getName());

	private final int minWidth = 696;
	
	private JPanel contentPane;
	private JTextField txtSymbol;
	private JTextField txtQuantity;
	private JTextField txtPrice;
	private JTable table;
	private JTable table_1;
	private JTextField txtClient;
	private JLabel lblConnectionStatus;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainClient frame = new MainClient();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	public static void runGui(final GuiView guiView) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainClient frame = (MainClient) guiView;
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public MainClient() {
		addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent e) {
				Dimension size = getSize();
				if (size.width < minWidth) {
					setSize(minWidth, size.height);
				}
			}
		});
		setTitle("Stock exchange emulator");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 777, 454);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(new BorderLayout(0, 0));
		
		final JPanel panel = new JPanel();
		contentPane.add(panel, BorderLayout.SOUTH);
		
		JButton btnCancel = new JButton("Cancel");
		btnCancel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				for (int row : getTableOrders().getSelectedRows()) {
					String orderID = (String) getTableOrders().getValueAt(row, 0);
					if (listener != null) {
						listener.cancelOrder(orderID);
					}
				}
			}
		});
		
		txtClient = new JTextField();
		txtClient.setColumns(10);
		
		JLabel lblClientid = new JLabel("ClientID:");
		
		JButton btnNewButton = new JButton("Connect");
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String clientID = txtClient.getText();
				if (listener != null) {
					listener.connect(clientID);
				}
			}
		});
		
		lblConnectionStatus = new JLabel("Not connected");
		lblConnectionStatus.setForeground(Color.RED);
		
		JButton btnReplay = new JButton("Replay");
		btnReplay.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (getTableOrders().getSelectedRowCount() == 1) {
					DefaultTableModel model = (DefaultTableModel) getTableOrders().getModel();
					int row = getTableOrders().getSelectedRow();
					txtSymbol.setText((String) model.getValueAt(row, 1));
					txtQuantity.setText(String.valueOf(model.getValueAt(row, 2)));
					getCmbSide().setSelectedItem(model.getValueAt(row, 3));
					getCmbType().setSelectedItem(model.getValueAt(row, 4));
					if (getCmbType().getSelectedItem().equals(OrderType.LIMIT)) {
						txtPrice.setText(String.valueOf(model.getValueAt(row, 5)));
					}
				}
			}
		});
		GroupLayout gl_panel = new GroupLayout(panel);
		gl_panel.setHorizontalGroup(
			gl_panel.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel.createSequentialGroup()
					.addGap(5)
					.addComponent(lblClientid)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(txtClient, GroupLayout.PREFERRED_SIZE, 62, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(btnNewButton)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(lblConnectionStatus)
					.addPreferredGap(ComponentPlacement.RELATED, 255, Short.MAX_VALUE)
					.addComponent(btnReplay)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(btnCancel, GroupLayout.PREFERRED_SIZE, 85, GroupLayout.PREFERRED_SIZE)
					.addContainerGap())
		);
		gl_panel.setVerticalGroup(
			gl_panel.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
					.addComponent(lblClientid)
					.addComponent(txtClient, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
					.addComponent(btnNewButton)
					.addComponent(lblConnectionStatus)
					.addComponent(btnCancel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
					.addComponent(btnReplay))
		);
		gl_panel.setAutoCreateGaps(true);
		panel.setLayout(gl_panel);
		
		JPanel panel_1 = new JPanel();
		contentPane.add(panel_1, BorderLayout.NORTH);
		
		JLabel lblSymbol = new JLabel("Symbol:");
		
		txtSymbol = new JTextField();
		txtSymbol.setColumns(10);
		
		JLabel lblQuantity = new JLabel("Quantity:");
		
		txtQuantity = new JTextField();
		txtQuantity.setColumns(10);
		
		JLabel lblSide = new JLabel("Side:");
		
		cmbSide = new JComboBox();
		cmbSide.setModel(new DefaultComboBoxModel(OrderSide.values()));
		
		JLabel lblType = new JLabel("Type:");
		
		cmbType = new JComboBox();
		cmbType.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				OrderType type = (OrderType) e.getItem();
				if (type == OrderType.LIMIT) {
					txtPrice.setEnabled(true);
				} else {
					txtPrice.setEnabled(false);
				}
			}
		});
		cmbType.setModel(new DefaultComboBoxModel(OrderType.values()));
		
		JLabel lblPrice = new JLabel("Price");
		
		txtPrice = new JTextField();
		txtPrice.setColumns(10);
		
		if ((OrderType) cmbType.getSelectedItem() == OrderType.MARKET) {
			txtPrice.setEnabled(false);
		} else {
			txtPrice.setEnabled(true);
		}
		
		JButton btnSubmit = new JButton("Submit");
		btnSubmit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String ticket = txtSymbol.getText();
				OrderSide side = (OrderSide) cmbSide.getSelectedItem();
				
				int count;
				try {
					count = Integer.parseInt(txtQuantity.getText());
				} catch (NumberFormatException exception) {
					JOptionPane.showMessageDialog(panel, "Parsing quantity", "Create order", JOptionPane.ERROR_MESSAGE);
					return;
				}
				OrderType type = (OrderType) cmbType.getSelectedItem();
				float price;
				if (type == OrderType.LIMIT) {
					try {
						price = Float.parseFloat(txtPrice.getText());
					} catch (NumberFormatException exception) {
						JOptionPane.showMessageDialog(panel, "Parsing price", "Create order", JOptionPane.ERROR_MESSAGE);
						return;
					}
					
					if (listener != null) {
						listener.createOrder(ticket, side, price, count);
					}
				} else {
					if (listener != null) {
						listener.createOrder(ticket, side, count);
					}
				}
			}
		});
		GroupLayout gl_panel_1 = new GroupLayout(panel_1);
		gl_panel_1.setHorizontalGroup(
			gl_panel_1.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_1.createSequentialGroup()
					.addContainerGap()
					.addComponent(lblSymbol)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(txtSymbol, GroupLayout.PREFERRED_SIZE, 64, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(lblQuantity)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(txtQuantity, GroupLayout.PREFERRED_SIZE, 66, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(lblSide)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(cmbSide, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(lblType)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(cmbType, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(lblPrice)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(txtPrice, GroupLayout.DEFAULT_SIZE, 69, Short.MAX_VALUE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(btnSubmit, GroupLayout.PREFERRED_SIZE, 74, GroupLayout.PREFERRED_SIZE)
					.addContainerGap())
		);
		gl_panel_1.setVerticalGroup(
			gl_panel_1.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_1.createSequentialGroup()
					.addGap(15)
					.addGroup(gl_panel_1.createParallelGroup(Alignment.BASELINE)
						.addComponent(lblSymbol)
						.addComponent(txtSymbol, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(lblQuantity)
						.addComponent(txtQuantity, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(lblSide)
						.addComponent(cmbSide, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(lblType)
						.addComponent(cmbType, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(lblPrice)
						.addComponent(txtPrice, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(btnSubmit, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
					.addContainerGap())
		);
		gl_panel_1.setAutoCreateContainerGaps(true);
		panel_1.setLayout(gl_panel_1);
		
		JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
		contentPane.add(tabbedPane, BorderLayout.CENTER);
		
		JPanel panel_2 = new JPanel();
		tabbedPane.addTab("Orders", null, panel_2, null);
		panel_2.setLayout(new BorderLayout(0, 0));
		
		table = new JTable();
		table.setFillsViewportHeight(true);
		table.setModel(new DefaultTableModel(
			new Object[][] {
			},
			new String[] {
				"OrderID", "TicketSymbol", "Quantity", "Side", "Type", "Price", "Status", "Text"
			}
		) {
			Class[] columnTypes = new Class[] {
				String.class, String.class, Integer.class, Object.class, Object.class, Object.class, Object.class, String.class
			};
			public Class getColumnClass(int columnIndex) {
				return columnTypes[columnIndex];
			}
			boolean[] columnEditables = new boolean[] {
				false, false, false, false, false, false, false, false
			};
			public boolean isCellEditable(int row, int column) {
				return columnEditables[column];
			}
		});

		panel_2.add(table.getTableHeader(), BorderLayout.PAGE_START);
		panel_2.add(table);
		
		JPanel panel_3 = new JPanel();
		tabbedPane.addTab("Executions", null, panel_3, null);
		panel_3.setLayout(new BorderLayout(0, 0));
		
		table_1 = new JTable();
		table_1.setFillsViewportHeight(true);
		table_1.setModel(new DefaultTableModel(
			new Object[][] {
			},
			new String[] {
				"OrderID", "Last quantity", "Last price", "PartyID", "Status"
			}
		) {
			Class[] columnTypes = new Class[] {
				String.class, Integer.class, Object.class, String.class, Object.class
			};
			public Class getColumnClass(int columnIndex) {
				return columnTypes[columnIndex];
			}
			boolean[] columnEditables = new boolean[] {
				false, false, false, false, false
			};
			public boolean isCellEditable(int row, int column) {
				return columnEditables[column];
			}
		});
		panel_3.add(table_1.getTableHeader(), BorderLayout.PAGE_START);
		panel_3.add(table_1);
	}

	@Override
	public void logon() {
		getLblConnectionStatus().setText("Logon");		
	}

	@Override
	public void logout() {
		getLblConnectionStatus().setText("Logout");		
	}

	@Override
	public void reject(RejectReason reason, String error) {
		if (reason == RejectReason.FAILED_ID) {
			getLblConnectionStatus().setText("Failed logon: " + error);
		} else {
			JOptionPane.showMessageDialog(this, error, reason.toString(), JOptionPane.WARNING_MESSAGE);
		}		
	}
	
	public JLabel getLblConnectionStatus() {
		return lblConnectionStatus;
	}

	@Override
	public void addOrder(ClientOrder order) {
		log.info("For gui: add order [" + order.getOrderID() + "]");
		
		DefaultTableModel model = (DefaultTableModel) getTableOrders().getModel();
		model.addRow(new Object[] {
				order.getOrderID(), 
				order.getTicketSymbol(),
				order.getQuantity(),
				order.getSide(),
				order.getType(),
				order.getPrice(),  
				order.getStatus(),
				order.getText()
			});	
	}

	@Override
	public void changeStatusOrder(ClientOrder order) {
		log.info("For gui: change status order [" + order.getOrderID() + "] - " + order.getStatus());
		
		DefaultTableModel model = (DefaultTableModel) getTableOrders().getModel();
		
		for (int i = 0; i < model.getRowCount(); i++) {
			if (model.getValueAt(i, 0).equals(order.getOrderID())) {
				model.setValueAt(order.getStatus(), i, 6);
				if (order.getStatus() == OrderStatus.REJECTED) {
					model.setValueAt(order.getText(), i, 7);
				}
				break;
			}
		}
		
	}

	@Override
	public void addExecution(Execution execution) {
		log.info("For gui: add execution");
		DefaultTableModel model = (DefaultTableModel) getTableExecutions().getModel();
		model.addRow(new Object[] {
				execution.getOrderID(), 
				execution.getQuantity(),
				execution.getPrice(),
				execution.getOtherClientID(), 
				execution.getStatus()
			});
		
	}

	private GuiListener listener;
	private JComboBox cmbSide;
	private JComboBox cmbType;
	
	@Override
	public void setListener(GuiListener listener) {
		this.listener = listener;
	}
	
	public JTable getTableOrders() {
		return table;
	}
	public JTable getTableExecutions() {
		return table_1;
	}
	
	public JComboBox getCmbSide() {
		return cmbSide;
	}
	public JComboBox getCmbType() {
		return cmbType;
	}
}
